#import "SearchService.h"
#import "WebService.h"
#import "Cliente.h"
#import "Fabrica.h"
#import "Vendedor.h"
#import "Transportadora.h"
#import "Pedido.h"
#import "Produto.h"
#import "AuthenticationError.h"

@interface SearchService()<SettingsListener>
@property (nonatomic, strong) WebService* webService;
@property (nonatomic, strong) Settings* settings;
@end

@implementation SearchService

@synthesize settings;
@synthesize webService;

+(SearchService*)searchServiceWithSettings:(Settings *)settings {
    return [[SearchService alloc] initWithSettings:settings];
}

-(void)createWebService {
    self.webService = [WebService webServiceWithUrl:[self.settings.url stringByAppendingString:@"/services/searchservice.asmx"]];
}

-(id)initWithSettings:(Settings *)aSettings {
    self = [super init];
    if (self) {
        self.settings = aSettings;
        [self createWebService];
        [aSettings subscribeToChanges:self];
    }
    return self;
}

+(NSError*)verifyError:(NSError*)error {
    if (error && [[error localizedDescription] rangeOfString:@"RestrictedAreaException"].location != NSNotFound) {
        return [AuthenticationError authenticationError];
    }
    return error;
}

-(BOOL)settingsChanged:(Settings *)settings error:(NSError **)error {
    [self logout];
    [self createWebService];
    return [self login:error];
}

-(BOOL)login:(NSError**)error {
    id result = [self.webService sendRequestFor:@"Login" withValues:@{@"username":self.settings.username, @"password":self.settings.password} error:error];
    return (*error == nil && [result isKindOfClass:[NSString class]] && [result isEqualToString:@"true"]);
}

-(void)logout {
    NSError* error;
    [self.webService sendRequestFor:@"Logout" error:&error];
}

typedef id(^GetModels)(NSError**);

-(id)getModelsAndLogin:(NSError**)error getModels:(GetModels)getModels {
    NSError* innerError;
    id result = getModels(&innerError);
    innerError = [SearchService verifyError:innerError];
    if (!innerError || *error) {
        *error = innerError;
        return result;
    }
    
    NSError* loginError;
    [self logout];
    BOOL login = [self login:&loginError];
    loginError = [SearchService verifyError:loginError];
    if (loginError || !login) {
        *error = loginError ? loginError : innerError;
        return nil;
    }
    
    result = [self getModelsAndLogin:&innerError getModels:getModels];
    *error = [SearchService verifyError:innerError];
    return result;
}

-(NSArray*)getClientes:(NSError**)error {
    return [self getModelsAndLogin:error getModels:^NSArray *(NSError** innerError) {
        return [self.webService getModelsForAction:@"GetClientes" 
                                      withKeyName:@"ClienteContract" 
                       convertFromDictionaryBlock:^id(NSDictionary* dictionary) {
                           Cliente* cliente = [[Cliente alloc] init];
                           cliente.id = [dictionary objectForKey:@"Id"];
                           cliente.razaoSocial = [dictionary objectForKey:@"RazaoSocial"];
                           cliente.codigoJolitex = [dictionary objectForKey:@"CodigoJolitex"];
                           cliente.vendedorId = [dictionary objectForKey:@"VendedorId"];
                           return cliente;
                       } error:innerError];
    }];
}

-(NSArray*)getFabricas:(NSError**)error {
    return [self getModelsAndLogin:error getModels:^NSArray *(NSError** innerError) {
        return [self.webService getModelsForAction:@"GetFabricas" 
                                       withKeyName:@"FabricaContract" 
                        convertFromDictionaryBlock:^id(NSDictionary* dictionary) {
                            Fabrica* fabrica = [[Fabrica alloc] init];
                            fabrica.id = [dictionary objectForKey:@"Id"];
                            fabrica.nome = [dictionary objectForKey:@"Nome"];
                            return fabrica;
                        } error:innerError];
    }];
}

-(NSArray*)getVendedores:(NSError**)error {
    return [self getModelsAndLogin:error getModels:^NSArray *(NSError** innerError) {
        return [self.webService getModelsForAction:@"GetVendedores"
                                       withKeyName:@"VendedorContract" 
                        convertFromDictionaryBlock:^id(NSDictionary* dictionary) {
                            Vendedor* vendedor = [[Vendedor alloc] init];
                            vendedor.id = [dictionary objectForKey:@"Id"];
                            vendedor.nome = [dictionary objectForKey:@"Nome"];
                            return vendedor;
                        } error:innerError];
    }];
}

-(NSArray*)getTransportadoras:(NSError**)error {
    return [self getModelsAndLogin:error getModels:^NSArray *(NSError** innerError) {
        return [self.webService getModelsForAction:@"GetTransportadoras" 
                                       withKeyName:@"TransportadoraContract" 
                        convertFromDictionaryBlock:^id(NSDictionary* dictionary) {
                            Transportadora* transportadora = [[Transportadora alloc] init];
                            transportadora.id = [dictionary objectForKey:@"Id"];
                            transportadora.nome = [dictionary objectForKey:@"Nome"];
                            return transportadora;
                        } error:innerError];
    }];
}

- (Produto *)produtoFromDictionary:(NSDictionary *)dictionary {
    Produto* produto = [[Produto alloc] init];
    produto.codigo = [dictionary objectForKey:@"Codigo"];
    produto.descricao = [dictionary objectForKey:@"Descricao"];
    produto.precoUnitario = [[dictionary objectForKey:@"PrecoUnitario"] doubleValue];
    produto.quantidade = [[dictionary objectForKey:@"Quantidade"] intValue];
    return produto;
}

-(NSArray*)getLastPedidosForCliente:(Cliente*)cliente fabrica:(Fabrica*)fabrica vendedor:(Vendedor*)vendedor transportadora:(Transportadora*)transportadora error:(NSError**)error {
    NSDictionary* values = @{
         @"clienteId" : cliente ? cliente.id : @"",
         @"fabricaId" : fabrica ? fabrica.id : @"",
         @"vendedorId" : vendedor ? vendedor.id : @"",
         @"transportadoraId" : transportadora ? transportadora.id : @"",
         @"count" : @"30"
         };

    return [self getModelsAndLogin:error getModels:^NSArray *(NSError** innerError) {
        return [self.webService getModelsForAction:@"GetLastPedidos" withKeyName:@"PedidoInfoContract" withValues:values convertFromDictionaryBlock:^id(NSDictionary* dictionary){
            NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
            [dateFormatter setDateFormat:@"yyyy-MM-dd'T'hh:mm:ss"];
            Pedido* pedido = [[Pedido alloc] init];
            pedido.id = [dictionary objectForKey:@"Id"];
            pedido.numero = [[dictionary objectForKey:@"Numero"] intValue];
            pedido.data = [dateFormatter dateFromString:[dictionary objectForKey:@"Data"]];
            pedido.total = [[dictionary objectForKey:@"Total"] doubleValue];
            pedido.clienteId = [dictionary objectForKey:@"ClienteId"];
            pedido.fabricaId = [dictionary objectForKey:@"FabricaId"];
            pedido.vendedorId = [dictionary objectForKey:@"VendedorId"];
            pedido.transportadoraId = [dictionary objectForKey:@"TransportadoraId"];
            if ([[dictionary objectForKey:@"Produtos"] isKindOfClass:[NSString class]]) {
                pedido.produtos = @[];
            } else {
                id produtoContracts = [dictionary valueForKeyPath:@"Produtos.ProdutoContract"];
                if ([produtoContracts isKindOfClass:[NSArray class]]) {
                    NSMutableArray* produtos = [NSMutableArray arrayWithCapacity:[produtoContracts count]];
                    for (NSDictionary* produtoContract in produtoContracts) {
                        [produtos addObject:[self produtoFromDictionary:produtoContract]];
                    }
                    pedido.produtos = produtos;
                } else  {
                    pedido.produtos = @[[self produtoFromDictionary:produtoContracts]];
                }
            }
            return pedido;
        } error:innerError]; 
    }];
}

-(NSString*)getGetPedidoPrintHtml:(NSString *)pedidoId error:(NSError **)error {
    return [self getModelsAndLogin:error getModels:^id(NSError** innerError) {
        return [self.webService sendRequestFor:@"GetPedidoPrintHtml" withValues:@{@"pedidoId":pedidoId} error:innerError];
    }];
}

typedef InfoBase*(^CreateInfo)(NSDictionary* dictionary);

-(InfoBase*)getInfoById:(NSString *)infoId forAction:(NSString*)action withParameterName:(NSString*)parameterName createInfo:(CreateInfo)createInfo error:(NSError **)error {
    return [self getModelsAndLogin:error getModels:^id(NSError** innerError) {
        NSDictionary* infoContract = [self.webService sendRequestFor:action withValues:@{parameterName:infoId} error:innerError];
        if (*innerError) {
            return nil;
        }
        InfoBase* info = createInfo(infoContract);
        info.id = [infoContract objectForKey:@"Id"];;
        info.bairro = [infoContract objectForKey:@"Bairro"];
        info.cep = [infoContract objectForKey:@"CEP"];
        info.celular = [infoContract objectForKey:@"Celular"];
        info.cidade = [infoContract objectForKey:@"Cidade"];
        info.complemento = [infoContract objectForKey:@"Complemento"];
        info.email = [infoContract objectForKey:@"Email"];
        info.estado = [infoContract objectForKey:@"Estado"];
        info.fax = [infoContract objectForKey:@"Fax"];
        info.numero = [infoContract objectForKey:@"Numero"];
        info.rua = [infoContract objectForKey:@"Rua"];
        info.telefone = [infoContract objectForKey:@"Telefone"];
        return info;
    }];
}

-(ClienteInfo*)getClienteInfoById:(NSString *)clienteId error:(NSError **)error {
    return (ClienteInfo*)[self getInfoById:clienteId forAction:@"GetClienteInfo" withParameterName:@"clienteId" createInfo:^InfoBase *(NSDictionary *dictionary) {
        ClienteInfo* clienteInfo = [[ClienteInfo alloc] init];
        clienteInfo.cnpj = [dictionary objectForKey:@"CNPJ"];
        clienteInfo.inscricaoEstadual = [dictionary objectForKey:@"InscricaoEstadual"];
        clienteInfo.nomeFantasia = [dictionary objectForKey:@"NomeFantasia"];
        return clienteInfo;
    } error:error];
}

-(VendedorInfo*)getVendedorInfoById:(NSString *)vendedorId error:(NSError **)error {
    return (VendedorInfo*)[self getInfoById:vendedorId forAction:@"GetVendedorInfo" withParameterName:@"vendedorId" createInfo:^InfoBase *(NSDictionary *dictionary) {
        VendedorInfo* vendedorInfo = [[VendedorInfo alloc] init];
        vendedorInfo.cpf = [dictionary objectForKey:@"CPF"];
        vendedorInfo.identidade = [dictionary objectForKey:@"Identidade"];
        return vendedorInfo;
    } error:error];
}

-(FabricaInfo*)getFabricaInfoById:(NSString *)fabricaId error:(NSError **)error {
    return (FabricaInfo*)[self getInfoById:fabricaId forAction:@"GetFabricaInfo" withParameterName:@"fabricaId" createInfo:^InfoBase *(NSDictionary *dictionary) {
        FabricaInfo* fabricaInfo = [[FabricaInfo alloc] init];
        fabricaInfo.cnpj = [dictionary objectForKey:@"CNPJ"];
        fabricaInfo.inscricaoEstadual = [dictionary objectForKey:@"InscricaoEstadual"];
        return fabricaInfo;
    } error:error];
}

-(TransportadoraInfo*)getTransportadoraInfoById:(NSString *)transportadoraId error:(NSError **)error {
    return (TransportadoraInfo*)[self getInfoById:transportadoraId forAction:@"GetTransportadoraInfo" withParameterName:@"transportadoraId" createInfo:^InfoBase *(NSDictionary *dictionary) {
        return [[TransportadoraInfo alloc] init];
    } error:error];
}

@end